package com.mask.token.service;

import com.mask.token.config.MaskTokenProperties;
import com.mask.token.model.MaskUserDetails;
import com.mask.token.model.TokenInfo;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import javax.crypto.spec.SecretKeySpec;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.util.StringUtils;

/**
 * Token服务类
 * 提供JWT Token的生成、验证、刷新等功能
 *
 * @author mask
 * @since 1.0.0
 */
@Slf4j
public class MaskTokenService {

    private final MaskTokenProperties properties;
    private final MaskTokenRedisService redisService;
    private final MaskTokenKeyService keyService;
    
    public MaskTokenService(MaskTokenProperties properties, MaskTokenRedisService redisService, MaskTokenKeyService keyService) {
        this.properties = properties;
        this.redisService = redisService;
        this.keyService = keyService;
    }
    
    // 简化构造函数，用于测试
    public MaskTokenService(MaskTokenProperties properties) {
        this.properties = properties;
        this.redisService = null;
        this.keyService = null;
    }
    
    /**
     * 存储用户Token信息，用于多端登录管理（内存存储作为备用）
     */
    private final Map<String, TokenInfo> tokenStore = new ConcurrentHashMap<>();

    /**
     * 生成JWT密钥
     */
    private SecretKey getSigningKey() {
        try {
            // 如果密钥管理服务可用，使用密钥管理服务获取安全的密钥
            if (keyService != null) {
                return keyService.getSigningKey();
            }
        } catch (Exception e) {
            log.warn("使用密钥管理服务失败，回退到简单密钥生成: {}", e.getMessage());
        }
        
        // 回退到原来的简单密钥生成方式
        byte[] keyBytes = properties.getSecret().getBytes(StandardCharsets.UTF_8);
        if (keyBytes.length < 32) {
            byte[] paddedKey = new byte[32];
            System.arraycopy(keyBytes, 0, paddedKey, 0, Math.min(keyBytes.length, 32));
            keyBytes = paddedKey;
        }
        return new SecretKeySpec(keyBytes, SignatureAlgorithm.HS512.getJcaName());
    }

    /**
     * 生成Access Token
     *
     * @param userDetails 用户详情
     * @return Access Token
     */
    public String generateAccessToken(MaskUserDetails userDetails) {
        return generateToken(userDetails, properties.getAccessTokenValidity());
    }

    /**
     * 生成Refresh Token
     *
     * @param userDetails 用户详情
     * @return Refresh Token
     */
    public String generateRefreshToken(MaskUserDetails userDetails) {
        return generateToken(userDetails, properties.getRefreshTokenValidity());
    }

    /**
     * 生成Token
     *
     * @param userDetails 用户详情
     * @param validity 有效期
     * @return Token
     */
    private String generateToken(MaskUserDetails userDetails, java.time.Duration validity) {
        Instant now = Instant.now();
        Instant expiry = now.plus(validity);

        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userDetails.getUserId());
        claims.put("username", userDetails.getUsername());
        claims.put("authorities", userDetails.getAuthorities());
        claims.put("deviceId", userDetails.getDeviceId());
        claims.put("loginTime", now.toEpochMilli());

        String token = Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(Date.from(now))
                .setExpiration(Date.from(expiry))
                .signWith(SignatureAlgorithm.HS512, getSigningKey())
                .compact();

        // 存储Token信息
        TokenInfo tokenInfo = new TokenInfo();
        tokenInfo.setToken(token);
        tokenInfo.setUserId(userDetails.getUserId());
        tokenInfo.setUsername(userDetails.getUsername());
        tokenInfo.setDeviceId(userDetails.getDeviceId());
        tokenInfo.setLoginTime(now);
        tokenInfo.setExpireTime(expiry);
        
        // 优先使用Redis存储，如果Redis不可用则使用内存存储
        if (redisService != null) {
            try {
                redisService.storeToken(tokenInfo, validity);
            } catch (Exception e) {
                log.warn("Redis存储失败，使用内存存储: {}", e.getMessage());
                tokenStore.put(token, tokenInfo);
            }
        } else {
            // Redis服务不可用，直接使用内存存储
            tokenStore.put(token, tokenInfo);
        }

        log.debug("为用户 {} 生成Token，设备ID: {}", userDetails.getUsername(), userDetails.getDeviceId());
        return token;
    }

    /**
     * 验证Token
     *
     * @param token Token字符串
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        try {
            if (!StringUtils.hasText(token)) {
                return false;
            }

            // 首先验证JWT签名和格式（这是最重要的验证）
            Claims claims = Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .parseClaimsJws(token)
                    .getBody();

            // 检查JWT是否过期
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                log.warn("JWT Token已过期: {}", token);
                removeTokenFromStorage(token);
                return false;
            }

            // 尝试从存储中获取Token信息（用于额外验证和统计）
            TokenInfo tokenInfo = null;
            if (redisService != null) {
                try {
                    tokenInfo = redisService.getTokenInfo(token);
                } catch (Exception e) {
                    log.debug("从Redis获取Token信息失败，尝试内存存储: {}", e.getMessage());
                    tokenInfo = tokenStore.get(token);
                }
            } else {
                // Redis服务不可用，直接从内存存储获取
                tokenInfo = tokenStore.get(token);
            }

            // 如果Token不在存储中，但JWT有效，说明可能是重启后的情况
            // 这种情况下，我们仍然认为Token有效，但会记录警告
            if (tokenInfo == null) {
                log.warn("Token不在存储中，但JWT有效（可能是重启后情况）: {}", token);
                // 可以选择是否将Token重新存储到缓存中
                // 这里我们选择不重新存储，因为JWT本身已经足够验证
            } else {
                // 如果Token在存储中，检查存储的过期时间
                if (tokenInfo.getExpireTime().isBefore(Instant.now())) {
                    log.warn("存储中的Token已过期: {}", token);
                    removeTokenFromStorage(token);
                    return false;
                }
            }

            return true;
        } catch (Exception e) {
            log.warn("Token验证失败: {}, 错误: {}", token, e.getMessage());
            removeTokenFromStorage(token);
            return false;
        }
    }

    /**
     * 从Token中获取用户名
     *
     * @param token Token字符串
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .parseClaimsJws(token)
                    .getBody();
            return claims.getSubject();
        } catch (Exception e) {
            log.warn("从Token获取用户名失败: {}, 错误: {}", token, e.getMessage());
            return null;
        }
    }

    /**
     * 从Token中获取用户ID
     *
     * @param token Token字符串
     * @return 用户ID
     */
    public Long getUserIdFromToken(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(getSigningKey())
                    .parseClaimsJws(token)
                    .getBody();
            return claims.get("userId", Long.class);
        } catch (Exception e) {
            log.warn("从Token获取用户ID失败: {}, 错误: {}", token, e.getMessage());
            return null;
        }
    }

    /**
     * 刷新Token
     *
     * @param token 原Token
     * @param userDetails 用户详情
     * @return 新的Access Token
     */
    public String refreshToken(String token, MaskUserDetails userDetails) {
        if (!validateToken(token)) {
            throw new IllegalArgumentException("无效的Token");
        }

        // 删除原Token
        removeTokenFromStorage(token);

        // 生成新Token
        return generateAccessToken(userDetails);
    }

    /**
     * 注销Token
     *
     * @param token Token字符串
     */
    public void logout(String token) {
        removeTokenFromStorage(token);
        log.debug("Token已注销: {}", token);
    }

    /**
     * 注销用户所有Token
     *
     * @param userId 用户ID
     */
    public void logoutAll(Long userId) {
        if (redisService != null) {
            try {
                redisService.removeUserAllTokens(userId);
            } catch (Exception e) {
                log.warn("从Redis删除用户所有Token失败，使用内存存储: {}", e.getMessage());
                tokenStore.entrySet().removeIf(entry -> 
                    entry.getValue().getUserId().equals(userId));
            }
        } else {
            // Redis服务不可用，直接从内存存储删除
            tokenStore.entrySet().removeIf(entry -> 
                entry.getValue().getUserId().equals(userId));
        }
        log.debug("用户 {} 的所有Token已注销", userId);
    }

    /**
     * 检查Token是否需要续期
     *
     * @param token Token字符串
     * @return 是否需要续期
     */
    public boolean shouldRenewToken(String token) {
        if (!properties.isAutoRenewal()) {
            return false;
        }

        TokenInfo tokenInfo = getTokenInfo(token);
        if (tokenInfo == null) {
            return false;
        }

        Instant now = Instant.now();
        Instant renewalTime = tokenInfo.getExpireTime().minus(properties.getRenewalThreshold());
        
        return now.isAfter(renewalTime);
    }

    /**
     * 获取Token信息
     *
     * @param token Token字符串
     * @return Token信息
     */
    public TokenInfo getTokenInfo(String token) {
        // 首先尝试从存储中获取
        TokenInfo tokenInfo = null;
        if (redisService != null) {
            try {
                tokenInfo = redisService.getTokenInfo(token);
            } catch (Exception e) {
                log.debug("从Redis获取Token信息失败，使用内存存储: {}", e.getMessage());
                tokenInfo = tokenStore.get(token);
            }
        } else {
            // Redis服务不可用，直接从内存存储获取
            tokenInfo = tokenStore.get(token);
        }

        // 如果存储中没有Token信息，但JWT有效，从JWT中提取信息
        if (tokenInfo == null) {
            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(getSigningKey())
                        .parseClaimsJws(token)
                        .getBody();

                // 检查JWT是否过期
                Date expiration = claims.getExpiration();
                if (expiration.before(new Date())) {
                    log.warn("JWT Token已过期: {}", token);
                    return null;
                }

                // 从JWT中构建TokenInfo
                tokenInfo = new TokenInfo();
                tokenInfo.setToken(token);
                tokenInfo.setUserId(claims.get("userId", Long.class));
                tokenInfo.setUsername(claims.getSubject());
                tokenInfo.setDeviceId(claims.get("deviceId", String.class));
                
                // 从JWT中提取时间信息
                Long loginTimeMillis = claims.get("loginTime", Long.class);
                if (loginTimeMillis != null) {
                    tokenInfo.setLoginTime(Instant.ofEpochMilli(loginTimeMillis));
                }
                tokenInfo.setExpireTime(expiration.toInstant());
                tokenInfo.setLastActiveTime(Instant.now());

                log.debug("从JWT中提取Token信息: {}", token);
            } catch (Exception e) {
                log.warn("从JWT提取Token信息失败: {}", e.getMessage());
                return null;
            }
        }

        return tokenInfo;
    }

    /**
     * 获取用户在线设备数量
     *
     * @param userId 用户ID
     * @return 在线设备数量
     */
    public int getOnlineDeviceCount(Long userId) {
        if (redisService != null) {
            try {
                return redisService.getUserOnlineDeviceCount(userId);
            } catch (Exception e) {
                log.warn("从Redis获取在线设备数量失败，使用内存存储: {}", e.getMessage());
                return (int) tokenStore.values().stream()
                        .filter(tokenInfo -> tokenInfo.getUserId().equals(userId))
                        .count();
            }
        } else {
            // Redis服务不可用，直接从内存存储获取
            return (int) tokenStore.values().stream()
                    .filter(tokenInfo -> tokenInfo.getUserId().equals(userId))
                    .count();
        }
    }

    /**
     * 获取用户所有Token信息
     *
     * @param userId 用户ID
     * @return Token信息列表
     */
    public List<TokenInfo> getUserAllTokens(Long userId) {
        if (redisService != null) {
            try {
                return redisService.getUserAllTokens(userId);
            } catch (Exception e) {
                log.warn("从Redis获取用户Token失败，使用内存存储: {}", e.getMessage());
                return tokenStore.values().stream()
                        .filter(tokenInfo -> tokenInfo.getUserId().equals(userId))
                        .collect(java.util.stream.Collectors.toList());
            }
        } else {
            // Redis服务不可用，直接从内存存储获取
            return tokenStore.values().stream()
                    .filter(tokenInfo -> tokenInfo.getUserId().equals(userId))
                    .collect(java.util.stream.Collectors.toList());
        }
    }

    /**
     * 从存储中移除Token
     *
     * @param token Token字符串
     */
    private void removeTokenFromStorage(String token) {
        if (redisService != null) {
            try {
                redisService.removeToken(token);
            } catch (Exception e) {
                log.warn("从Redis删除Token失败，使用内存存储: {}", e.getMessage());
                tokenStore.remove(token);
            }
        } else {
            // Redis服务不可用，直接从内存存储删除
            tokenStore.remove(token);
        }
    }

    /**
     * 清理过期的Token
     */
    public void cleanExpiredTokens() {
        if (redisService != null) {
            try {
                redisService.cleanExpiredTokens();
            } catch (Exception e) {
                log.warn("Redis清理过期Token失败，使用内存存储: {}", e.getMessage());
                // 清理内存中的过期Token
                tokenStore.entrySet().removeIf(entry -> 
                    entry.getValue().getExpireTime().isBefore(Instant.now()));
            }
        } else {
            // Redis服务不可用，直接清理内存中的过期Token
            tokenStore.entrySet().removeIf(entry -> 
                entry.getValue().getExpireTime().isBefore(Instant.now()));
        }
    }

    /**
     * 获取Token剩余过期时间
     *
     * @param token Token字符串
     * @return 剩余过期时间（秒）
     */
    public long getTokenExpireTime(String token) {
        if (redisService != null) {
            try {
                return redisService.getTokenExpireTime(token);
            } catch (Exception e) {
                log.warn("从Redis获取Token过期时间失败: {}", e.getMessage());
                TokenInfo tokenInfo = tokenStore.get(token);
                if (tokenInfo != null) {
                    return java.time.Duration.between(Instant.now(), tokenInfo.getExpireTime()).getSeconds();
                }
                return -1;
            }
        } else {
            // Redis服务不可用，直接从内存存储获取
            TokenInfo tokenInfo = tokenStore.get(token);
            if (tokenInfo != null) {
                return java.time.Duration.between(Instant.now(), tokenInfo.getExpireTime()).getSeconds();
            }
            return -1;
        }
    }

    /**
     * 获取Redis连接信息
     *
     * @return Redis连接信息
     */
    public String getRedisInfo() {
        if (redisService != null) {
            try {
                return redisService.getRedisInfo();
            } catch (Exception e) {
                return "Redis不可用: " + e.getMessage();
            }
        } else {
            return "Redis服务未配置";
        }
    }
}
